package com.lagou.zkcode.initzk;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lagou.zkcode.ZkStrSerializer;
import com.lagou.zkcode.config.JDBCConfig;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * zookeeper读取数据库信息，并设置监听
 *
 * @author lichunyu
 * @since 2022/3/6
 */
@Slf4j
@Component
public class ZkInfoInit implements ApplicationRunner {

    private static HikariDataSource hikariDataSource;

    private static ZkClient zkClient;

    private static ObjectMapper mapper = new ObjectMapper();

    @Override
    public void run(ApplicationArguments args) throws Exception {

        initZkClient();

        initDataSource();


    }

    private void initDataSource() {

        configHikariSource();

    }

    /**
     * 初始化zk客户端，并对节点进行监听
     *
     * @author lichunyu
     * @since 2022/3/8
     */
    private void initZkClient() {

        // 加载zk客户端
        zkClient = new ZkClient("172.16.187.122:2181");

        // 设置序列化
        zkClient.setZkSerializer(new ZkStrSerializer());

        // 设置zk的数据监听
        zkClient.subscribeDataChanges("/jdbc", new IZkDataListener() {

            public void handleDataChange(String path, Object data) {

                log.info("{}监听的数据已经改变，新的数据为：{}", path, data);

                // 关闭数据库连接池
                hikariDataSource.close();

                configHikariSource();
            }

            public void handleDataDeleted(String path) {
                log.info("数据已经被删除");
                hikariDataSource.close();
            }
        });


    }

    /**
     * 读取数据库配置并执行SQL
     *
     * @author lichunyu
     * @since 2022/3/8
     */
    private static void configHikariSource() {

        // 读取数据库配置并更新连接池
        JDBCConfig myConfig = getJDBCConfig();
        updateHikariConfig(myConfig);

        try {
            // 执行查询语句
            executeTestSQL();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库配置对象
     *
     * @return JDBCConfig
     * @author lichunyu
     * @since 2022/3/8
     */
    private static JDBCConfig getJDBCConfig() {

        Object data = zkClient.readData("/jdbc");

        try {
            JDBCConfig myConfig = mapper.readValue(data.toString(), JDBCConfig.class);
            log.info("mysql存储在zk的配置信息：{}", myConfig.toString());

            return myConfig;

        } catch (JsonProcessingException e) {

            return new JDBCConfig();
        }
    }

    /**
     * 更新数据库连接配置
     * @author lichunyu
     * @since  2022/3/8
     * @param myConfig
     */
    private static void updateHikariConfig(JDBCConfig myConfig) {

        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(myConfig.getUrl());
        config.setUsername(myConfig.getUsername());
        config.setPassword(myConfig.getPassword());
        config.addDataSourceProperty("driverClassName", myConfig.getDriver());
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        hikariDataSource = new HikariDataSource(config);
    }

    /**
     * 执行SQL语句
     * @author lichunyu
     * @since  2022/3/8
     */
    private static void executeTestSQL() throws SQLException {

        Connection connection = hikariDataSource.getConnection();

        PreparedStatement pst = connection.prepareStatement("SELECT id, username FROM user;");

        ResultSet rs = pst.executeQuery();

        while (rs.next()) {
            log.info("id : {}, username:{}", rs.getString(1) ,  rs.getString(2));
        }
    }
}
